image001.png

Исследование набора данных журнала Scientific Reports https://www.nature.com/srep/

Поляков Евгений, НТИМИ, 2019

Содержание

Введение

О ресурсе Nature Research

Scientific Reports - журнал с откртытым доступом, который публикует оригинальные исследования из различных областей естественных и клинических наук, который предлагает своим авторам высоко уважаемое место для своих исследовваний. Scientific Reports - одиннадцатый по цитируемости журнал в мире, с показателями больше чем 300 000 цитат в 2018 году. Журнал получает широкое внимание в политике и средствах массовой информации.

Цель исследования

Цель данного исследования подразумевает получение промежуточных знаний, необходимых для будущего решения более сложных задач, связанных с областью науки о данных в сфере научного знания. Scientific Reports на ряду с другими источниками науныных данных представляет интерес для понимания общей тенденции происходящей в мировой науке. Изучение тематических состввляющих, периодов публикации, авторского состава и других особенностей авторов и их работ, может послужить более правильному пониманию области и вырабатать эффективные шаги для последующего решания различных задача связанных с обработкой естественного языка применительно к научным публикациям.

Настройка среды

Подключение библиотек

In [1]:
import ast
import calendar
import re
from datetime import datetime
from glob import glob

import cufflinks as cf
import dateparser
import folium
import nltk
import numpy as np
import pandas as pd
import plotly
import plotly.figure_factory as ff
import plotly.graph_objs as go
import plotly.tools as tls
from folium.plugins import MarkerCluster
from IPython.display import HTML, Markdown, clear_output, display
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from plotly.offline import init_notebook_mode, iplot
from py_linq import Enumerable
from tqdm import notebook, tqdm
from sklearn.feature_extraction.text import CountVectorizer

init_notebook_mode(connected=True)
cf.go_offline(connected=True)
cf.set_config_file(world_readable=True, theme='white')
# cf.getThemes()

Настройка библиотек

In [2]:
nltk.download('stopwords')
nltk.download('punkt')
[nltk_data] Downloading package stopwords to
[nltk_data]     /home/polyakov/nltk_data...
[nltk_data]   Package stopwords is already up-to-date!
[nltk_data] Downloading package punkt to /home/polyakov/nltk_data...
[nltk_data]   Package punkt is already up-to-date!
Out[2]:
True

Общий код

In [3]:
class PlotlyDashboard:
    def __init__(self,
                 rows: int = 1,
                 cols: int = 1,
                 print_grid=False,
                 width=900,
                 height=500,
                 title='Dashboard 1',
                 params: dict = {}):
        self.dashboard = None

        self.rows = rows
        self.cols = cols
        self.print_grid = print_grid
        self.width = width
        self.height = height
        self.title = title
        self.params = params

        self._create()

    def _create(self):
        self.dashboard = plotly.subplots.make_subplots(rows=self.rows,
                                           cols=self.cols,
                                           print_grid=self.print_grid,
                                           **self.params)
        self.dashboard['layout'].update(height=self.height,
                                        width=self.width,
                                        title_text=self.title)

    def add_board(self,
                  trace: any,
                  row: int = 1,
                  col: int = 1,
                  layout_params: dict = {}):
        self.dashboard.add_trace(trace=trace, row=row, col=col)
        if layout_params != None:
            for k, v in layout_params.items():
                self.dashboard['layout'][k].update(**v)

        return self

    def show(self):
        self.dashboard.show()

Анализ данных

Загрузка данных

Обзор данных на диске

In [4]:
!ls -lha 'data/'
total 9,6G
drwxr-xr-x 6 polyakov polyakov  4,0K ноя 24 11:12 .
drwxr-xr-x 4 polyakov polyakov  4,0K ноя 26 15:49 ..
-rw-r--r-- 1 polyakov polyakov  532M ноя 19 14:08 biological_sciences_metadata.csv
-rw-r--r-- 1 polyakov polyakov   21K ноя 22 14:43 counties_code.csv
-rw-r--r-- 1 polyakov polyakov  8,4K ноя 22 16:42 countries.csv
drwxr-xr-x 2 polyakov polyakov  4,0K ноя 24 11:47 img
drwxr-xr-x 2 polyakov polyakov  4,0K ноя 14 11:53 .ipynb_checkpoints
-rw-r--r-- 1 polyakov polyakov  785M ноя 19 14:02 physical_sciences_metadata.csv
drwxr-xr-x 2 polyakov polyakov  572K ноя 19 14:02 scientific_reports__biological_sciences_meta
-rw-r--r-- 1 polyakov polyakov  115M ноя 19 12:36 scientific_reports__biological_sciences.tar.xz
-rw-r--r-- 1 polyakov polyakov  3,6G ноя 23 17:10 scientific_reports_metadata.csv
drwxr-xr-x 2 polyakov polyakov 1012K ноя 15 15:18 scientific_reports__physical_sciences_meta
-rw-r--r-- 1 polyakov polyakov  3,6G ноя 22 21:49 sr_metadata.csv
-rw-r--r-- 1 polyakov polyakov  967M ноя 22 22:30 sr_metadata.xlsx
-rw-r--r-- 1 polyakov polyakov  145M ноя 22 15:04 worldcitiespop.csv

В качестве набора данных для журнала Scientific Reports представлен файл в формате '.csv' - scientific_reports_metadata.csv, размером 3.6 Гб

Загрузка данных журнала

In [5]:
sr = pd.read_csv('data/scientific_reports_metadata.csv')

Обзор части данных

In [6]:
sr.head(3).iloc[0]
Out[6]:
create_date                            2019-11-19 16:01:04.824327
journal_name    scientific_reports__earth_and_environmental_sc...
publish_date                                        08 March 2019
type                                                      Article
title           Spatio-temporal patterns and characteristics o...
authors         ['Erin E. Gorsich', 'Ryan S. Miller', 'Holly M...
abstract        Domestic swine production in the United States...
affilations     ['Department of Biology, Colorado State Univer...
aff_authors     ['Erin E. Gorsich,\xa0Ryan S. Miller,\xa0Holly...
subjects                              ['Ecology', 'Risk factors']
biblio          ['Received14 May 2018', 'Accepted24 January 20...
text            ['Swine production in the United States is a 1...
file_path       /scientific_reports__earth_and_environmental_s...
country_name    United States of America###United States of Am...
country_lat     37.09024###37.09024###55.378051###55.378051###...
country_lon     -95.712891###-95.712891###-3.435973###-3.43597...
city_name       Fort Collins###Fort Collins###Coventry###Coven...
city_lat        40.5852778###40.5852778###52.416667###52.41666...
city_lon        -105.08388889999999###-105.08388889999999###-1...
Name: 0, dtype: object

Список переменных

In [7]:
display(HTML('|| '.join([f'<b>{str(x)}</b>' for x in sr.columns])))
create_date|| journal_name|| publish_date|| type|| title|| authors|| abstract|| affilations|| aff_authors|| subjects|| biblio|| text|| file_path|| country_name|| country_lat|| country_lon|| city_name|| city_lat|| city_lon

Описание признаков

  • create_date - дата загрузки статьи
  • journal_name - название журнала
  • publish_date - дата выхода статьи в журнале
  • type - тип статьи в семействе Nature
  • title - заголовок статьи
  • authors - авторы статьи
  • abstract - аннотация статьи
  • affilations - аффиляции авторов
  • aff_authors - авторы по аффеляции
  • subjects - тематика
  • biblio - информация по публикации
  • text - текст статьи
  • file_path - имя файла на диске
  • country_name - названия стран извлеченных из аффиляции авторов в порядке перечисления авторов
  • country_lat - значения широты стран авторов из аффиляции(в порядке перечисления авторов)
  • country_lon - значения долготы стран авторов из аффиляции (в порядке перечисления авторов)
  • city_name - названия городов извлеченных из аффиляции авторов (в порядке перечисления авторов)
  • city_lat - значения широты городов авторов из аффиляции(в порядке перечисления авторов)
  • city_lon - значения долготы городов авторов из аффиляции (в порядке перечисления авторов)

Первичный анализ данных

Форма данных

In [10]:
sr.shape
Out[10]:
(105324, 19)

Набор данных содержит 105324 статей и 19 признаковых описаний

Дубликаты

Полные дубликаты
In [11]:
print("Количество полнстью дублирубщих значений: ",
      len(sr) - len(sr.drop_duplicates()))
Количество полнстью дублирубщих значений:  0
Дублткаты по заголовку
In [12]:
print("Количество дубликатов по заголовку: ",
      len(sr) - len(sr.drop_duplicates(subset=['title'])))
Количество дубликатов по заголовку:  28925

Количечтво дублирующих заголовков 28925. Выглядит странно. Возможно журнал отнес одну и ту же статью к разным разделам, т.е. статья одновременно относиться к разным тематикам. Проверим это предположение.

In [14]:
# найдем дубликаты
duplicates = sr[sr.duplicated(subset=['title'])]
# возьмем один дубликат, найдем его в основном наборе данных, 
# посмотрим на различие в остальных метаданных
display(sr[sr['title'] == duplicates.iloc[0]['title']])
del(duplicates)
create_date journal_name publish_date type title authors abstract affilations aff_authors subjects biblio text file_path country_name country_lat country_lon city_name city_lat city_lon
6856 2019-11-20 07:32:01.096978 scientific_reports__earth_and_environmental_sc... 07 March 2017 Article Daytime warming has stronger negative effects ... ['Xiumin Yan', 'Kehong Wang', 'Lihong Song', '... Warming of the climate system is unequivocal, ... ['School of Geography and Tourism, Guizhou Edu... ['Xiumin Yan', 'Xiumin Yan,\xa0Kehong Wang,\xa... ['Biodiversity', 'Climate-change ecology'] ['Received13 October 2016', 'Accepted14 Februa... ['Warming of the global land surface, due to t... /scientific_reports__earth_and_environmental_s... China###China###China###China###China 35.86166###35.86166###35.86166###35.86166###35... 104.195397###104.195397###104.195397###104.195... Guiyang###Changchun###Chongqing###Guiyang###Ch... 26.583333000000003###43.88###29.562778###26.58... 106.71666699999999###125.322778###106.552778##...
7720 2019-11-20 07:05:58.033564 scientific_reports__earth_and_environmental_sc... 20 March 2017 Article Daytime warming has stronger negative effects ... ['Xiumin Yan', 'Kehong Wang', 'Lihong Song', '... Warming of the climate system is unequivocal, ... ['School of Geography and Tourism, Guizhou Edu... ['Xiumin Yan', 'Xiumin Yan,\xa0Kehong Wang,\xa... ['Biodiversity', 'Climate-change ecology'] ['Received13 October 2016', 'Accepted14 Februa... ['Warming of the global land surface, due to t... /scientific_reports__earth_and_environmental_s... China###China###United States of America###Chi... 35.86166###35.86166###37.09024###35.86166###35... 104.195397###104.195397###-95.712891###104.195... Guiyang###Changchun###Chongqing###Guiyang###Ch... 26.583333000000003###43.88###30.08333###26.583... 106.71666699999999###125.322778###107.83333###...
78453 2019-11-14 12:12:13.652297 scientific_reports__biological_sciences 07 March 2017 Article Daytime warming has stronger negative effects ... ['Xiumin Yan', 'Kehong Wang', 'Lihong Song', '... Warming of the climate system is unequivocal, ... ['School of Geography and Tourism, Guizhou Edu... ['Xiumin Yan', 'Xiumin Yan,\xa0Kehong Wang,\xa... ['Biodiversity', 'Climate-change ecology'] ['Received13 October 2016', 'Accepted14 Februa... ['Warming of the global land surface, due to t... /scientific_reports__biological_sciences/s4159... China###China###China###China###China 35.86166###35.86166###35.86166###35.86166###35... 104.195397###104.195397###104.195397###104.195... Guiyang###Changchun###Chongqing###Guiyang###Ch... 26.583333000000003###43.88###29.562778###26.58... 106.71666699999999###125.322778###106.552778##...

Первый пример показал, что существует одна и та же статья, но в разных журналах, и время публикации тоже отличается в одном из журналов. Посчитаем статистически в скольких разных журналах в среднем повторяется каждая неуникальная статья.

In [176]:
duplicates = sr[sr.duplicated(subset=['title'])]
journal_name_stat = []
for dup_title in notebook.tqdm(list(duplicates['title'])):
    journal_name_stat.append(len(sr[sr['title'] == dup_title]['journal_name']))
# Считаем средние значения
print('среднее значение повторяемости неуникальной статьи: ',
      np.mean(journal_name_stat))
del (journal_name_stat, duplicates, dup_title)
среднее значение повторяемости неуникальной статьи:  2.124114088159032

В среднем статья повторияется в 2-х разных журналах. Так как среднее значение больше 2-x, значит бывает что и в 3-х. Интересная особенность журнала - публиковать в 2-х или 3-х разных тематических разделах одну и ту же статью.

Дубликаты по остальным переменным
In [16]:
print(
    "Дубликаты по многим переменным: ",
    len(sr) - len(
        sr.drop_duplicates(subset=[
            'journal_name', 'title', 'authors', 'abstract', 'affilations',
            'aff_authors', 'subjects', 'biblio'
        ])))
Дубликаты по многим переменным:  0

По большинству основных переменных дубликатов нет.

Такие статьи скорее всего нужно оставлять, так как они по видимому относятся к нескольким разделам, что тоже важно при выявлении тематик статьи.

Null-значения

In [17]:
pd.DataFrame({"NaN": sr.isna().sum(), 'None': (sr == 'None').sum()})
Out[17]:
NaN None
create_date 0 0
journal_name 0 0
publish_date 0 0
type 0 0
title 0 0
authors 0 0
abstract 0 0
affilations 0 0
aff_authors 0 0
subjects 0 0
biblio 0 0
text 0 27
file_path 0 0
country_name 1 128
country_lat 1 128
country_lon 1 128
city_name 1 258
city_lat 1 258
city_lon 1 258

Некоторые переменные имеют null-значения. Можно в будущем удалить статьи где в этих переменных нет данных.

In [18]:
sr_clearning_vars = [
    'text', 'country_name', 'country_lat', 'country_lon', 'city_name',
    'city_lat', 'city_lon'
]

Анализ по переменным

create_date

Посмотрим на переменную обозначающую дату создания записи

In [19]:
sr.create_date[:10]
Out[19]:
0    2019-11-19 16:01:04.824327
1    2019-11-19 11:38:58.614246
2    2019-11-19 14:02:11.876860
3    2019-11-20 06:10:27.878388
4    2019-11-19 18:47:37.322139
5    2019-11-20 04:41:02.796187
6    2019-11-19 13:14:56.995706
7    2019-11-20 20:01:36.979317
8    2019-11-20 05:53:19.565803
9    2019-11-20 23:31:03.182192
Name: create_date, dtype: object

Переменная показывает дату скачивания статьи скриптом, и никак не связана с отальными данными статьи. Такую переменную можно убрать из дальнейшего анализа.

In [20]:
sr_dropping_vars = ['create_date']
sr_dropping_vars = list(set(sr_dropping_vars))
publish_date, biblio

В переменной biblio, есть дата публикации статьи, посмотрим совпадает ли эта дата с датой из переменной publish_date

In [21]:
for col in ['publish_date', 'biblio']:
    display(HTML(f"<b>{col}</b>"))
    display(sr.iloc[0][col])
publish_date
'08 March 2019'
biblio
"['Received14 May 2018', 'Accepted24 January 2019', 'Published08 March 2019', 'DOIhttps://doi.org/10.1038/s41598-019-40556-z']"

переменная biblio, содержит в себе значение переменной из publish date, поэтому наверное эта переменная может быть удалена из переменных для дальнейшего анализа.

In [22]:
sr_dropping_vars.append('publish_date')
sr_dropping_vars = list(set(sr_dropping_vars))
type

Переменна type содержит тип статьи. Определим, сколько типов статей есть в данном журнале

In [23]:
sr['type'].value_counts()
Out[23]:
Article                  105309
Conference Proceeding        15
Name: type, dtype: int64

Журнал имеет 2 типа статей, второй тип Conference Proceeding, встречается всего 15 раз. Найдем эти 15 статей и посмотрим на их остальные данные.

In [24]:
sr.drop(sr_dropping_vars, axis=1)[sr.drop(sr_dropping_vars, axis=1)['type'] ==
                                  'Conference Proceeding'][:3]
Out[24]:
journal_name type title authors abstract affilations aff_authors subjects biblio text file_path country_name country_lat country_lon city_name city_lat city_lon
2044 scientific_reports__earth_and_environmental_sc... Conference Proceeding Potassium Niobate Nanolamina: A Promising Adso... ['Jin Sun', 'Dongjiang Yang', 'Cuihua Sun', 'L... Processing and managing radioactive waste is a... ['Collaborative Innovation Centre for Marine B... ['Jin Sun,\xa0Dongjiang Yang,\xa0Cuihua Sun\xa... ['Environmental sciences', 'Materials science'] ['Received01 September 2014', 'Accepted13 Nove... ['With increasing applications of nuclear tech... /scientific_reports__earth_and_environmental_s... China###China###China###Australia###United Kin... 35.86166###35.86166###35.86166###-25.274398###... 104.195397###104.195397###104.195397###133.775... Shandong###Qingdao###Changsha###Brisbane###Bir... 28.661944000000002###36.098611###30.741667###-... 121.46###120.371944###117.463889###153.024292#...
2660 scientific_reports__earth_and_environmental_sc... Conference Proceeding Remediation of chromium-slag leakage with elec... ['Binbin Yu', 'Huimin Zhang', 'Wei Xu', 'Gang ... Chromium pollution has been historically wides... ['Department of Environmental Engineering, Lab... ['Binbin Yu,\xa0Huimin Zhang,\xa0Wei Xu,\xa0Ga... ['Electrochemistry', 'Pollution remediation'] ['Received28 February 2014', 'Accepted23 May 2... ['Chromium was widely used in industry for the... /scientific_reports__earth_and_environmental_s... China###China 35.86166###35.86166 104.195397###104.195397 Hangzhou###Hangzhou 30.29365###30.29365 120.161419###120.161419
3739 scientific_reports__earth_and_environmental_sc... Conference Proceeding Platinum particles supported on mesoporous car... ['Cheng-Di Dong', 'Chiu-Wen Chen', 'Chih-Feng ... In this report, we describe the preparation an... ['Department of Marine Environmental Engineeri... ['Cheng-Di Dong,\xa0Chiu-Wen Chen,\xa0Chih-Fen... ['Fuel cells', 'Pollution remediation'] ['Received12 February 2014', 'Accepted02 June ... ["Methanol (CH3OH) has attracted increasing at... /scientific_reports__earth_and_environmental_s... Taiwan, Province of China 23.69781 120.96051499999999 Taiwan 23.210277800000004 120.18416670000002

Отличий по типу статьи никаких не выявлено, можно удалить переменну, не удаляя стати принадлежащие к этому типу.

In [25]:
sr_dropping_vars.append('type')
sr_dropping_vars = list(set(sr_dropping_vars))
affilations, country_name, country_lat, country_lon, city_name, city_lat, city_lon

В описании написано, что из аффиляции были извлечены переменные: country_name, country_lat, country_lon, city_name, city_lat, city_lon

In [26]:
sr.drop(sr_dropping_vars, axis=1)[[
    'affilations', 'country_name', 'country_lat', 'country_lon', 'city_name',
    'city_lat', 'city_lon'
]][:3]
Out[26]:
affilations country_name country_lat country_lon city_name city_lat city_lon
0 ['Department of Biology, Colorado State Univer... United States of America###United States of Am... 37.09024###37.09024###55.378051###55.378051###... -95.712891###-95.712891###-3.435973###-3.43597... Fort Collins###Fort Collins###Coventry###Coven... 40.5852778###40.5852778###52.416667###52.41666... -105.08388889999999###-105.08388889999999###-1...
1 ['Lawrence Berkeley National Laboratory, Earth... United States of America 37.09024 -95.712891 Berkeley 37.8716667 -122.27166670000001
2 ['Departamento de Microbiología, Instituto de ... Uruguay###United States of America###Uruguay##... -32.522779###37.09024###-32.522779###-32.522779 -55.765834999999996###-95.712891###-55.7658349... Montevideo###Maryland###Uruguay###Montevideo -34.85805560000001###42.0655556###-32.4833333#... -56.1708333###-89.54722220000001###-53.5166667...

Выходит, что теперь можно удалить переменную affilations и пользоваться извлеченными переменными.

In [27]:
sr_dropping_vars.append('affilations')
sr_dropping_vars = list(set(sr_dropping_vars))
authors, aff_authors
In [28]:
for col in ['authors', 'aff_authors', 'affilations']:
    display(HTML(f"<b>{col}</b>"))
    display(ast.literal_eval(sr.iloc[2][col]))
authors
['B. Branchiccela',
 'L. Castelli',
 'M. Corona',
 'S. Díaz-Cetti',
 'C. Invernizzi',
 'G. Martínez de la Escalera',
 'Y. Mendoza',
 'E. Santos',
 'C. Silva',
 'P. Zunino',
 'K. Antúnez']
aff_authors
['B. Branchiccela,\xa0L. Castelli,\xa0G. Martínez de la Escalera,\xa0P. Zunino\xa0&\xa0K. Antúnez',
 'M. Corona',
 'S. Díaz-Cetti,\xa0Y. Mendoza\xa0&\xa0C. Silva',
 'C. Invernizzi\xa0&\xa0E. Santos']
affilations
['Departamento de Microbiología, Instituto de Investigaciones Biológicas Clemente Estable, Av. Italia 3318, CP 11,600, Montevideo, Uruguay',
 'Bee Research Laboratory United Stated Department of Agriculture, United States of America, Center Road 306, CP 20,705, Beltsville, Maryland, United States of America',
 'Sección Apicultura, Instituto de Investigación Agropecuaria, Route 50 km 11, CP 39173, Colonia, Uruguay',
 'Sección Etología, Instituto de Biología, Facultad de Ciencias, Iguá 4225, CP 11400, Montevideo, Uruguay']

Для примера у нас есть 11 авторов. Они из 4 разных университетов или других организаций, у них есть 4 записи аффиляции. В переменной aff_authors содержится перечисление авторов по афиляции, т.е. какие из этих 11 принадлежат к какой из аффиляций. Выходит, что authors тоже можно удалить, так как она дублирует авторов из aff_authors в неупорядоченном по аффиляции виде. Но по этой колонке можно понять первого автора, который всегда является ведущим. Возможно выясниться интересная закономерность по первому увтору. Оставим эту переменную.

title, abstract, text

Переменные title, abstract, text - представляют собой основну информацию по содержимому статьи, их нужно оставить для анализа текстовой составлящей. Причем переменная text - это массив, где содаржиться текст статьи разделенный на подразделы. Какой именно это раздел не понятно, но понятно что это раздел.

In [29]:
sr.drop(sr_dropping_vars, axis=1)[['title', 'abstract', 'text']].head(3)
Out[29]:
title abstract text
0 Spatio-temporal patterns and characteristics o... Domestic swine production in the United States... ['Swine production in the United States is a 1...
1 A quantitative method to decompose SWE differe... The simulation of snow water equivalent (SWE) ... ['Snowpack is a key component of the Earth’s c...
2 Impact of nutritional stress on the honeybee c... Honeybees Apis mellifera are important pollina... ['Insects play a significant role in the funct...
file_path, journal_name
In [30]:
sr.drop(sr_dropping_vars, axis=1)[['journal_name', 'file_path']].head(3)
Out[30]:
journal_name file_path
0 scientific_reports__earth_and_environmental_sc... /scientific_reports__earth_and_environmental_s...
1 scientific_reports__earth_and_environmental_sc... /scientific_reports__earth_and_environmental_s...
2 scientific_reports__earth_and_environmental_sc... /scientific_reports__earth_and_environmental_s...

file_path - путь к статье на диске, не очень полезная переменная для анализа, journal_name - имя журнала в котором размещена статья. Начало имени у всех журналов одинаковое, название можно сократить.

In [31]:
sr_dropping_vars.append('file_path')
sr_dropping_vars = list(set(sr_dropping_vars))

Итоги первичного анализа данных

  • Набор данных для анализа представлен в виде файла с расширением .csv с именем scientific_reports_metadata.csv, имеет размер 3.6 Гб, а также 4 папками именнованными названиями журналов размером ...
  • Представленный набор данных содержит 105324 - записи, и 19 переменных. В ходе процесса загрузки файлов журнала были определены следующие признаки, составляющие набор данных:

    • дата загрузки статьи [create_date] (время загрузки статьи с помощью скрипта)

    • название журнала [journal_name] (подраздел более общего журнала Scientific Reports)

    • дата публикации статьи в журнале [publish_date] (т.е. когда статья вошла в список статей журнала)

    • тип статьи [type] (тип данной статьи, т.е. такой какой она определяется на страничках журнала Nature)

    • заголовок статьи [title] (оригинальное название статьи), авторы [authors] (перечисление автора и соавторов)

    • аннотация [abstract] (аннотация статьи)

    • аффиляция [affilations] (аффиляция каждого из авторов статьи)

    • авторы по аффиляции [aff_authors] (перечисление авторов в порядке следования аффиляций)

    • тематика [subjects] (тематика или ключевые темы затронутые в статье помимо основной тематики журнала)

    • информация по публикации [biblio] (информация по ходу публикационной последовательности в журнале)

    • текст статьи [text] (массив с подразделами текста статьи)

    • путь к файлу [file_path] (путь к файлу .pdf статьи на диске)

    • названия стран [country_name] (название стран авторов по аффиляции)

    • значения широты страны [country_lat] (значение широты расположения страны на карте по аффиляции)

    • значение долготы [country_lon] (значения долготы расположения страны на карты по аффиляции)

    • названия городов [city_name] (название городов извлечённых из аффиляции авторов)

    • значения широты городов [city_lat] (значение широты расположения городов на карте по аффиляции)

    • значения долготы городов [city_lon] (значение долготы расположения городов на карте по аффиляции)

  • Набор данных не содержит полностью дублирующих значений, но существует 28925 дубликатов статей по заголовкам. Дальнейший анализ показал, что некоторые статьи одновременно принадлежат двум и более тематикам, среднее значение повторяемости статьи 2.12 т.е. некоторые статьи дублируются для 2 или 3 тематик одновременно.
  • Данные содержат небольшое количество Null значений (максимальное значение 258) в некоторых переменных (text, country_name, country_lat, country_lon, city_name, city_lat, city_lat). Объекты имеющие Null значения в этих переменных могут быть удалены.
  • Анализ отдельных переменных набора показал следующие особенности:

    • Переменная create_date показывающая время скачивания статьи не связана с другими переменными, может быть использована как показатель среднего времени, затрачиваемого на скачивание статьи при анализе последовательности значений, но данном анализе не представляет большого интереса.

    • Значение переменной publish_date, дублируется в переменной biblio, как одно из значений, и может быть удалена из дальнейшего анализа.

    • Тип статьи, обозначенный переменной type имеет большой перекос в сторону одного типа, а именно 105309 значений типа Article и 15 значений типа Conference Proceeding. Остальные переменные не имеют никакой связи с данной переменной, что не дает никакого полезного результата для анализа и может быть удалена как вся переменная, не затрагивая выборки по объектам.

    • Переменные affilations, country_name (1), country_lat (2) country_lon (3), city_name (4), city_lat (5), city_lon (6) - имеют прямую взаимосвязь, т.е. все переменные 2-6 являются результатом извлечения данных из переменной 1. Таким образом переменная 1 может быть удалена.

    • Авторы (authors) и авторы по аффиляции (aff_authors) - похожи друг на друга, так как содержат перечисления авторов в шапке статьи и авторов, расставленных в порядке аффиляции. Но переменная authors может быть использована для анализа страны ведущего автора, поэтому должна быть оставлена в наборе данных.

    • Основные переменные title, abstract, text - основное содержание статьи обязательно должны присутствовать в наборе.

    • Ссылка на .pdf файл статьи file_path) в данном анализе не представляется важной и может быть удалена. Переменная journal_name - важная переменная, показывающая основное направление деятельности ученых публикующих свои работы.

Подготовка дата-сета

Преобразование дата-сета учитывая итоги первичного анализа данных
In [32]:
# Удаление переменных с null-значениями
sr_clearning_vars = [
    'text', 'country_name', 'country_lat', 'country_lon', 'city_name',
    'city_lat', 'city_lon'
]
sr_tmp = sr.copy()
# Удаление значений с None
for col_for_clear in sr_clearning_vars:
    sr_tmp = sr_tmp[sr_tmp[col_for_clear] != 'None']
# Удаление значений с NaN
sr_tmp.dropna(inplace=True)
# Удаление переменных
sr_prepared = sr_tmp.drop(sr_dropping_vars, axis=1)

# Сокращение имени названия журанала
sr_prepared['journal_name'] = sr_prepared['journal_name'].apply(
    lambda x: x.split('__')[1])
del (col_for_clear, sr_tmp)
Проверка на Null
In [33]:
pd.DataFrame({
    "NaN": sr_prepared.isna().sum(),
    'None': (sr_prepared == 'None').sum()
})
Out[33]:
NaN None
journal_name 0 0
title 0 0
authors 0 0
abstract 0 0
aff_authors 0 0
subjects 0 0
biblio 0 0
text 0 0
country_name 0 0
country_lat 0 0
country_lon 0 0
city_name 0 0
city_lat 0 0
city_lon 0 0

Null-значений больше нет. Преобразование сделано верно

Итоговые статистики
Форма данных
In [34]:
sr_prepared.shape
Out[34]:
(105021, 14)
Итоговый дата-сет
In [35]:
sr_prepared.head(3)
Out[35]:
journal_name title authors abstract aff_authors subjects biblio text country_name country_lat country_lon city_name city_lat city_lon
0 earth_and_environmental_sciences Spatio-temporal patterns and characteristics o... ['Erin E. Gorsich', 'Ryan S. Miller', 'Holly M... Domestic swine production in the United States... ['Erin E. Gorsich,\xa0Ryan S. Miller,\xa0Holly... ['Ecology', 'Risk factors'] ['Received14 May 2018', 'Accepted24 January 20... ['Swine production in the United States is a 1... United States of America###United States of Am... 37.09024###37.09024###55.378051###55.378051###... -95.712891###-95.712891###-3.435973###-3.43597... Fort Collins###Fort Collins###Coventry###Coven... 40.5852778###40.5852778###52.416667###52.41666... -105.08388889999999###-105.08388889999999###-1...
1 earth_and_environmental_sciences A quantitative method to decompose SWE differe... ['Yun Xu', 'Andrew Jones', 'Alan Rhoades'] The simulation of snow water equivalent (SWE) ... ['Yun Xu,\xa0Andrew Jones\xa0&\xa0Alan Rhoades'] ['Cryospheric science', 'Hydrology'] ['Received24 January 2019', 'Accepted23 Octobe... ['Snowpack is a key component of the Earth’s c... United States of America 37.09024 -95.712891 Berkeley 37.8716667 -122.27166670000001
2 earth_and_environmental_sciences Impact of nutritional stress on the honeybee c... ['B. Branchiccela', 'L. Castelli', 'M. Corona'... Honeybees Apis mellifera are important pollina... ['B. Branchiccela,\xa0L. Castelli,\xa0G. Martí... ['Microbial ecology', 'Pathogens'] ['Received28 January 2019', 'Accepted20 June 2... ['Insects play a significant role in the funct... Uruguay###United States of America###Uruguay##... -32.522779###37.09024###-32.522779###-32.522779 -55.765834999999996###-95.712891###-55.7658349... Montevideo###Maryland###Uruguay###Montevideo -34.85805560000001###42.0655556###-32.4833333#... -56.1708333###-89.54722220000001###-53.5166667...

Визуальный анализ данных

Гипотреза 1. Распределение статей по тематикам журналов неравномерно

Предполагается, что каждая тема статьи значительно отличается по популярности от других тем. Построим распределение статей из набора данных по разным тематикам (журналам) из общего набора Scientific Reports и визуализируем результаты.

In [173]:
(PlotlyDashboard(
    rows=1,
    cols=1,
    height=500,
    width=900,
    title='<b>Распределение статей по журналам</b>',
    params=dict(
        vertical_spacing=0.3,
        horizontal_spacing=0.1,
    )).add_board(go.Bar(
        x=[x for x in list(sr_prepared['journal_name'].value_counts().index)],
        y=list(sr_prepared['journal_name'].value_counts().values),
        name='Deep Learning',
        orientation='v',
    ),
                 row=1,
                 col=1,
                 layout_params=dict(xaxis1=dict(title_text="Журнал",
                                                automargin=True),
                                    yaxis1=dict(title_text="Статьи",
                                                automargin=False)))).show()

Распределение статей по журналам показывает, что популярность разных тематик неодинакова. Тема биологии (biological_sciences) занимает лидирующее место среди 4 представленных тематик и имеет порядка 39000 статей. В порядке убывания по популярности расположились науки о здоровье (health_sciences), физические науки (physical_sciences) и науки о земле и окружающей среде (earth_and_environmental_sciences), с количеством статей порядка 33000, 22000 и 12000 соответственно.

Гипотеза 2. Вероятность публикации дубликата статьи в другом журнале зависит от его тематики

Результаты предварительного анализа показали, что существуют статьи которые дублируются в двух разных журналах с разными тематиками в семействе Scientific Reropts. Посчитаем вероятоности попадания в один из дополнительных журналов при публикации статьи по одной из тематик.

In [38]:
# Имена журналов
j_names = list(sr_prepared.journal_name.unique())
# Заголовки дубликатов статей

journals_stats = {}
for i, j_name in enumerate(j_names):  # Проход по журналам
    duplicates = sr_prepared[sr_prepared.duplicated(subset=['title'])][[
        'title', 'journal_name'
    ]]
    # Ищем дубликаты и фильтруем по журналу
    duplicates = duplicates[duplicates['journal_name'] == j_name]
    journal_stats_tmp = {}
    # Проходим по дубликатам журнала
    for a_title, j_name in zip(duplicates['title'],
                               duplicates['journal_name']):
        # Выбираем статьи которые дублируют эту, и откидываем
        # дубликат к этому журналу
        jn_list = list(sr_prepared[(sr_prepared['title'] == a_title)
                                   & (sr_prepared['journal_name'] != j_name)]
                       ['journal_name'])
        for jn in jn_list:
            # Пытаемся найти ключ журнала в словаре
            if journal_stats_tmp.get(jn, 'Not_Found') == 'Not_Found':
                journal_stats_tmp[jn] = 1
            else:  # Нашли ключ
                # увеличиваем значение счетчика
                journal_stats_tmp[jn] = (journal_stats_tmp[jn] + 1)
    journals_stats[j_name] = {
        k: v / np.sum(list(journal_stats_tmp.values())) * 100
        for k, v in journal_stats_tmp.items()
    }
pd.DataFrame(journals_stats).fillna(0).T.iplot(
        kind='bar',
        yTitle='Статьи, %',
        xTitle='Журнал',
        title='Частота публикации дубликатов в других журналах')
del (j_names, journals_stats, i, j_name, duplicates, journal_stats_tmp,
     a_title, jn_list, jn)
  • Предварительный анализ набора данных показал, что статьи дублируются по заголовкам, что привело к выводу, что редакция журнала публикует дубликаты в нескольких разделах. Визуализация результатов определила, что вероятность печати дубликата статьи в другом журнале составляет:
    • Для темы earth_and_environmental_sciences - 100% статей публикуются так же и в журнале biological_sciences
    • Для темы health_sciences - вероятность публикации дубликата составляет 25% для biological_sciences, 65% для earth_and_environmental_sciences, 9% для physical_sciences
    • Для темы physical_sciences - вероятность публикации дубликата составляет 18% для biological_sciences, 36% для earth_and_environmental_sciences, 44% для health_sciences
    • Для темы biological_sciences - вероятность публикации составляет 18% для earth_and_environmental_sciences, 17% для physical_sciences, 64% для health_sciences

Гипотеза 3. Среднее значение количества авторов статьи зависит от тематики журнала

Так как специфики работы над научными задачами различны для каждой из областей знаний, в среде ученых складываются определенные подходы к решению задач. Они могут заключаться к привлечению других ученых которые вместе работают над публикациями. Предполагается, что для каждой из областей

In [39]:
sr_prepared_tmp = sr_prepared[['authors', 'journal_name']].copy()
sr_prepared_tmp['author_count'] = sr_prepared_tmp['authors'].apply(
    lambda x: len(ast.literal_eval(x)))
# Имена журналов
j_names = list(sr_prepared.journal_name.unique())
journals_stats = {}
for j_name in j_names:
    journals_stats[j_name] = sr_prepared_tmp[sr_prepared_tmp['journal_name'] ==
                                             j_name]['author_count'].mean()
pd.Series(journals_stats).iplot(
    kind='bar',
    yTitle='Авторы',
    xTitle='Журнал',
    title='Распределение журналов по среднему количеству авторов')
del(sr_prepared_tmp, j_name, j_names)

Больше всего авторов в среднем на каждую статью приходиться в журналах связанных со здоровьем и медициной и меньше в журналах связанных с естестенными науками.

Гипотеза 4. Нибольшее число публикаций сосредаточено вокруг некоторого числа популярных тематик

Построим распределение статей по популярным тематикам в процентах

In [40]:
subject_distrib = pd.Series(
    Enumerable(sr_prepared['subjects'].apply(lambda x: ast.literal_eval(x))).
    select_many(lambda x: x).to_list()).value_counts()
for j_count, j_title in zip([len(subject_distrib), 50], [
        'Распределение статей по тематикам (все тематики)',
        'Распределение статей по тематикам (первые 50)'
]):
    (PlotlyDashboard(rows=1,
                     cols=1,
                     height=500,
                     width=900,
                     title=j_title,
                     params=dict(
                         vertical_spacing=0.3,
                         horizontal_spacing=0.1,
                     )).add_board(go.Bar(
                         x=subject_distrib.index[:j_count],
                         y=(subject_distrib.values /
                            np.sum(subject_distrib.values) * 100)[:j_count],
                         name='Deep Learning',
                         orientation='v',
                     ),
                                  row=1,
                                  col=1,
                                  layout_params=dict(
                                      xaxis1=dict(title_text="тематика",
                                                  automargin=True),
                                      yaxis1=dict(
                                          title_text="количество статей",
                                          automargin=False)))).show()

Из распределения видно, что нет никакой доминирующей тематики, самая популярная тематика покрывает чуть больше 1% работ. Эти тематики судя по всему влются ключевыми словами.

Гипотеза 5. Количество публикуемых статей с годами растет, статьи обычно публикуют больше в опредененные месяцы года, и определенные дни недели

Построим распределение количества статей по разным годам, месяцам и дням недели

In [41]:
# сделаем копию набора данных для манипуляций
sr_prepared_tmp = sr_prepared.copy()
# Выделим в отдельные переменные дату получеия статьи, принятия в печать,
# и публикации в журнале
# (pos - позиция в массиве, name - название столбца который будет создан,
# length - длина для отрезания слова до даты)
action_type = ['received', 'accepted', 'published']
for pos, name, length in zip([0, 1, 2], action_type, [8, 8, 9]):
    sr_prepared_tmp[name] = sr_prepared_tmp['biblio'].apply(
        lambda x: ast.literal_eval(x)[pos][length:])


def _get_years_stat(df: pd.DataFrame, data_col: str):
    """Возвращает год отсортированные по убыванию"""
    return df[data_col].apply(
        lambda x: dateparser.parse(x).year).value_counts().sort_index()


def _get_month_stat(df: pd.DataFrame, data_col: str):
    months = df[data_col].apply(
        lambda x: dateparser.parse(x).month).value_counts().sort_index()
    return pd.Series(data=months.values,
                     index=[calendar.month_name[x] for x in months.index])


def _get_weekday_stat(df: pd.DataFrame, data_col: str):
    weekdays = df[data_col].apply(
        lambda x: dateparser.parse(x).weekday()).value_counts().sort_index()
    wd_name_arr = []
    for wd in weekdays.index:
        wd_name = {
            i: weekday_name
            for i, weekday_name in enumerate(list(calendar.day_name))
        }.get(wd)
        wd_name_arr.append(wd_name)
    return pd.Series(data=weekdays.values, index=wd_name_arr)


for act_type, graph_title in zip(
        action_type,
    ['отправки', 'принятия в печать', 'публикации в журнале']):
    dashboard = PlotlyDashboard(
        rows=1,
        cols=3,
        height=400,
        width=900,
        title=f'Распределение статей по времени {graph_title}',
        params=dict(
            subplot_titles=[
                'Распределение по годам', 'Распределение по месяцам',
                'Распределение по дням недели'
            ],
            vertical_spacing=0.1,
            horizontal_spacing=0.1,
        ))

    for pos, func, xaxis, yaxis, xaxis_title in zip(
        [1, 2, 3], [_get_years_stat, _get_month_stat, _get_weekday_stat],
        ['xaxis1', 'xaxis2', 'xaxis3'], ['yaxis1', 'yaxis2', 'yaxis3'],
        ['год', 'месяц', 'день']):
        res = func(sr_prepared_tmp, act_type)
        dashboard.add_board(go.Scatter(x=res.index,
                                       y=res.values,
                                       name='',
                                       orientation='v',
                                       mode='lines+markers',
                                       line=dict(width=1),
                                       marker=dict(line=dict(width=1),
                                                   size=10,
                                                   color='gray'),
                                       showlegend=False),
                            row=1,
                            col=pos,
                            layout_params={
                                xaxis:
                                dict(title_text=xaxis_title, automargin=True),
                                yaxis:
                                dict(title_text="статьи", automargin=False)
                            })
    dashboard.show()
del (sr_prepared_tmp, action_type, pos, name, length, act_type, graph_title,
     dashboard, func, xaxis, yaxis, xaxis_title, res)
In [ ]:
 

Гипотеза 6. С течением времени некоторые темы становятся более популярными а некоторые менее популярными

Построим распределение тематик в зависимости от года печати. Посмотрим как меняется популярность тематик со временем

In [42]:
# сделаем копию набора данных для манипуляций
sr_prepared_tmp = sr_prepared.copy()
# Выделим в отдельные переменные дату получеия статьи
sr_prepared_tmp['received'] = sr_prepared_tmp['biblio'].apply(
    lambda x: ast.literal_eval(x)[0][8:])
# Имена журналов
j_names = list(sr_prepared.journal_name.unique())


def _get_years_stat(df: pd.DataFrame, data_col: str):
    """Возвращает год отсортированный по убыванию"""
    return df[data_col].apply(
        lambda x: dateparser.parse(x).year).value_counts().sort_index()


df = []
for j_name in j_names:
    res = _get_years_stat(
        sr_prepared_tmp[sr_prepared_tmp['journal_name'] == j_name],
        'received')
    df.append(pd.DataFrame({j_name: res.values}, index=res.index))
pd.concat(df,
          axis=1).fillna(0).iplot(yTitle='Статьи',
                                  xTitle='Год',
                                  title='Распределение получения по журналам',
                                  mode='lines+markers')
del (sr_prepared_tmp, j_names, df, j_name, res)

Результат показывает, что почти все журналы снчала набирают популярность, а потом начинуют ее терять.

Гипотеза 7. Количество времени ожидания рецензии отличается в зависимости от типа журнала

Построим график среднего времени ожидания ответа от рецензента в зависимости от года по каждому журналу

In [43]:
# сделаем копию набора данных для манипуляций
sr_prepared_tmp = sr_prepared.copy()
# Выделим в отдельные переменные дату получеия статьи, принятия в печать,
# и публикации в журнале
# (pos - позиция в массиве, name - название столбца который будет создан,
# length - длина для отрезания слова до даты)
action_type = ['received', 'accepted', 'published']
for pos, name, length in zip([0, 1, 2], action_type, [8, 8, 9]):
    sr_prepared_tmp[name] = sr_prepared_tmp['biblio'].apply(
        lambda x: ast.literal_eval(x)[pos][length:])
#tqdm.pandas(desc="my bar!")
# считаем сколько дней проходит от получения статьи редакцией до принятия в печать
sr_prepared_tmp['waiting_accepted_days'] = (
    sr_prepared_tmp['accepted'].apply(lambda x: dateparser.parse(x)) -
    sr_prepared_tmp['received'].apply(lambda x: dateparser.parse(x))
).apply(lambda x: x.days)
# Извлечение года публикации
sr_prepared_tmp['accepted_year'] = sr_prepared_tmp['accepted'].apply(
    lambda x: int(dateparser.parse(x).year))
# Имена журналов
j_names = list(sr_prepared.journal_name.unique())

df = []
for j_name in j_names:
    summary = sr_prepared_tmp[sr_prepared_tmp['journal_name'] == j_name][[
        'accepted_year', 'waiting_accepted_days'
    ]].groupby('accepted_year').mean()
    summary.index = [int(x) for x in summary.index]
    summary.rename(columns={'waiting_accepted_days': j_name}, inplace=True)
    df.append(summary)
pd.concat(df,
          axis=1).fillna(0).iplot(yTitle='Дни',
                                  xTitle='Год',
                                  title='Среднее время рецензирования статьи',
                                  mode='lines+markers')
del (sr_prepared_tmp, action_type, pos, name, length, j_names, df, j_name,
     summary)

Результаты показывают что с каждым голом практически равномерно растет время ожидания принятия статьи, кроме 2016 года, когда время немного сократилось. Если судить по рузультатам полученным выше, то можео заменить что 2016 год был самым продуктивным на количество присланных работ. Можно сделать вывод, что время рецензирования сократили из-за большого количества присланных статей. Но это только предположение. Есть интересный момент, что 2011 и 2012 году два журнала имели среднее время принятия в печать - 0. Извлечение данных за этот год показало, что для этих журналов в этих годах не было статей.

Гипотеза 8. В разных странах существует приоритет для изучения определенных видиов науки

In [ ]:
 
Распределение авторов публикаций по странам
In [ ]:
 
In [44]:
# Удалим статьи где из аффиляции не смогла извлечься страна
pd.Series(x for x in Enumerable(sr_prepared['country_name'].apply(
    lambda x: x.split('###'))).select_many(lambda x: x).to_list()
          if x != 'None').value_counts()[:25].iplot(
              kind='bar',
              yTitle='Статьи',
              xTitle='Страна',
              title='Распределение авторов по странам')

Китайских ученых больше всего учавствует в публикациях по данному журналу. Второе место занимает США, третье Япония.Россия занимает 19 строчку в рейтингах популярности журнала. В 1 статье может быть несколько соавторов, что может исказить рейтинг принадлежности к стране публикации. Например в одной публикации будет 3 автора из Китая и 1 из России, но результат будет засчитал в сторону Китая, так как в публикации учавствовало 3 автора. Это сравнение не полностью справедливо. Посчитаем только 1 автора из каждой страны, тогда получим правильное участие всех стран в публикациях.

In [45]:
# Удалим статьи где из аффиляции не смогла извлечься страна
pd.Series(x for x in Enumerable(sr_prepared['country_name'].apply(
    lambda x: np.unique(x.split('###')))).select_many(lambda x: x).to_list()
          if x != 'None').value_counts()[:25].iplot(
              kind='bar',
              yTitle='Статьи',
              xTitle='Страна',
              title='Распределение статей по странам')

Теперь видим, что Китай перешел на второе место, США - на первое. Россия стала ближе на 1 пункт переместившись на 18 место. Здесь можно сделата вывод, что Китай используети в своих работах больше в среднем соавторов чем США, Россия и другие страны.

Распределение стран по областям наук
In [46]:
# копия набора, для манипуляций над данными
sr_prepared_tmp = sr_prepared.copy()
# страны принимающие участие в публикациях
sr_prepared_tmp['countries_participant'] = sr_prepared_tmp[
    'country_name'].apply(lambda x: np.unique(x.split('###')))
# список направлений журналов
j_names = list(sr_prepared.journal_name.unique())

dfs = []
for j_name in j_names:  # Проход по журналам
    # считаем количество стран, которые используют технологии
    res = pd.Series(
        Enumerable(sr_prepared_tmp[sr_prepared_tmp['journal_name'] == j_name]
                   ['countries_participant']).select_many(
                       lambda x: x).to_list()).value_counts()
    dfs.append(pd.DataFrame({j_name: res.values}, index=res.index))
    # выбираем наиболее интересные страны
pd.concat(dfs, axis=1, sort=False).fillna(0).loc[[
    'China', 'United States of America',
    'United Kingdom of Great Britain and Northern Ireland', 'Germany',
    'France', 'Japan', 'Russian Federation', 'Italy', 'India', 'Sweden',
    'Netherlands', 'Brazil', 'Korea, Republic of', 'South Africa', 'Czechia',
    'Israel'
]].iplot(kind='bar',
         yTitle='Статьи',
         xTitle='Страна',
         title="Распределение стран по приоритетным областям наук")
del (sr_prepared_tmp, j_names, dfs, j_name, res)

Почти все страны главным приоритетом выбрали науки о здоровье и биологию, физические науки находятся ниже по рейтингу. Росиия отличается в обратную сторону, биологии и наукам о здоровье уделяется меньше времени чем физическим наукам.

Гипотеза 9. Привет

Распределение публикаций на карие мира

In [48]:
lat = [
    x for x in pd.Series(
        Enumerable(sr_prepared['country_lat'].apply(lambda x: x.split('###'))).
        select_many(lambda x: x).to_list()).values if x != 'None'
]

lon = [
    x for x in pd.Series(
        Enumerable(sr_prepared['country_lon'].apply(lambda x: x.split('###'))).
        select_many(lambda x: x).to_list()).values if x != 'None'
]

latitude = pd.Series(lat).value_counts()
longitude = pd.Series(lon).value_counts()

_map = folium.Map(location=[26.537737, 17.275546],
                  tiles='cartodbpositron',
                  zoom_start=2,
                  zoom_control=False)
folium.plugins.ScrollZoomToggler().add_to(_map)
marker_cluster = MarkerCluster().add_to(_map)
for _lat, _lon in zip(latitude.index, longitude.index):
    folium.Marker(location=[_lat, _lon],
                  icon=folium.Icon(icon=None, color='lightgray')).add_to(_map)
display(_map)
del (lat, lon, latitude, longitude, _map, marker_cluster, _lat, _lon)

На карте можно видеть, что авторы научных работ данного журнала есть практически со всех стран мира.

Распределение публикаций по городам США, Китая Индии и России

In [49]:
for county_name, country_coord, zoom in zip([
        'United States of America',
        'China',
        'India',
        'Russian Federation',
], [[39.942689, -97.448506], [34.435388, 106.189923], [22.537717, 79.209629],
        [63.020506, 93.065389]], [4, 4, 4, 3]):
    display(HTML(f'<h3>{county_name}</h3>'))
    cities_coord_arr = []
    for countries, cities, lats, lons in zip(
            sr_prepared['country_name'].apply(lambda x: x.split('###')),
            sr_prepared['city_name'].apply(lambda x: x.split('###')),
            sr_prepared['city_lat'].apply(lambda x: x.split('###')),
            sr_prepared['city_lon'].apply(lambda x: x.split('###'))):
        if county_name in countries:  # Если хоть одна страна есть в списке
            # Извлекаем индексы страны
            country_indexes = [
                i for i, x in enumerate(countries) if x == county_name
            ]
            for country_index in country_indexes:
                if lats[country_index] != 'None' and lons[
                        country_index] != 'None':
                    cities_coord_arr.append(
                        [lats[country_index], lons[country_index]])
    _map = folium.Map(
        location=country_coord,
        # tiles='mapquestopen',
        zoom_start=zoom,
        zoom_control=False)
    folium.TileLayer('Stamen Toner').add_to(_map)
    folium.plugins.ScrollZoomToggler().add_to(_map)
    folium.plugins.Fullscreen().add_to(_map)
    marker_cluster = MarkerCluster().add_to(_map)
    for loc in list(pd.Series(cities_coord_arr).value_counts().index):
        folium.Marker(location=[loc[0], loc[1]],
                      icon=folium.Icon(icon='university',
                                       prefix='fa',
                                       color='blue')).add_to(_map)
    display(_map)
del (country_coord, zoom, cities_coord_arr, county_name, countries,
     country_indexes, country_index, _map, marker_cluster, loc)

United States of America

China

India

Russian Federation

Результаты визуализации показывают, что.

Распределение научных направлений по городам

In [50]:
country_names_arr = [
    'United States of America',
    'China',
    'India',
    'Russian Federation',
]
for county_name, country_coord, zoom in zip(
        country_names_arr, [[39.942689, -97.448506], [34.435388, 106.189923],
                            [22.537717, 79.209629], [63.020506, 93.065389]],
    [4, 4, 4, 3]):
    display(HTML(f'<h3>{county_name}</h3>'))
    cities_coord_arr = []  # координаты городов
    journal_names_arr = []  # названия журналов
    for countries, cities, lats, lons, journal in zip(
            sr_prepared['country_name'].apply(lambda x: x.split('###')),
            sr_prepared['city_name'].apply(lambda x: x.split('###')),
            sr_prepared['city_lat'].apply(lambda x: x.split('###')),
            sr_prepared['city_lon'].apply(lambda x: x.split('###')),
            sr_prepared['journal_name']):
        if county_name in countries:  # Если хоть одна страна есть в списке
            # Извлекаем индексы страны
            country_indexes = [
                i for i, x in enumerate(countries) if x == county_name
            ]
            for country_index in country_indexes:
                # Проверяем чтобы не было пустых кординат,
                # и имена городов не назывались как страны
                if lats[country_index] != 'None' and lons[
                        country_index] != 'None' and cities[
                            country_index] not in country_names_arr + [
                                'Russia'
                            ]:
                    cities_coord_arr.append(
                        [lats[country_index], lons[country_index]])
                    journal_names_arr.append(journal)

    _map = folium.Map(
        location=country_coord,
        #tiles='mapquestopen',
        zoom_start=zoom,
        zoom_control=False)
    folium.TileLayer('cartodbpositron').add_to(_map)
    folium.plugins.ScrollZoomToggler().add_to(_map)
    folium.plugins.Fullscreen().add_to(_map)
    # Карта цветов для области
    color_map = {
        'biological_sciences': 'orange',
        'physical_sciences': 'lightblue',
        'health_sciences': 'purple',
        'earth_and_environmental_sciences': 'darkgreen'
    }
    # соединяем координаты городов с их научными направлениями,
    # считаем количество научных работ по направлению для города
    science_result = pd.Series([
        f'{str(k)}###{str(v)}'
        for k, v in zip(journal_names_arr, cities_coord_arr)
    ]).value_counts()

    # Переведем в проценты
    science_result = science_result / science_result.sum() * 100
    # Для того чтобы не было слишком много объектов на карте,
    # отрежем 15% от конца самых незначительных
    science_result = science_result[:len(science_result) -
                                    int(len(science_result) * 0.15)]

    # Проходим по названию и направлению, и размеру направления
    for area_name_loc, size in zip(science_result.index,
                                   science_result.values):
        # Извлекаем координаты
        coord_city = ast.literal_eval(area_name_loc.split('###')[1])
        # Извлекаем область
        area = area_name_loc.split('###')[0]
        folium.CircleMarker(coord_city,
                            radius=size * 5,
                            color=color_map[area],
                            fill_color=color_map[area],
                            fill=True,
                            fill_opacity=0.7).add_to(_map)
    display(_map)
del (country_names_arr, county_name, country_coord, zoom, cities_coord_arr,
     journal_names_arr, countries, cities, lats, lons, journal,
     country_indexes, country_index, _map, color_map, science_result,
     area_name_loc, size, coord_city, area)

United States of America

China

India

Russian Federation

In [ ]:
 

Гипотеза 10. Распределение ключевых слов по тематикам

In [51]:
journal_names = list(sr_prepared['journal_name'].unique())
dashboard = PlotlyDashboard(rows=2,
                            cols=2,
                            height=800,
                            width=900,
                            title='Распределение ключевых слов по тематикам',
                            params=dict(vertical_spacing=0.53,
                                        horizontal_spacing=0.15,
                                        subplot_titles=journal_names))

for journal_name, row, col, xaxis, yaxis in zip(
        journal_names, [1, 1, 2, 2], [1, 2, 1, 2],
    ['xaxis1', 'xaxis2', 'xaxis3', 'xaxis4'],
    ['yaxis1', 'yaxis2', 'yaxis3', 'yaxis4']):
    journal_keys = pd.Series(
        Enumerable(
            sr_prepared[sr_prepared['journal_name'] == journal_name]
            ['subjects']).select(lambda x: ast.literal_eval(x)).select_many(
                lambda x: x).to_list()).value_counts()[:20]

    dashboard.add_board(go.Scatter(x=journal_keys.index,
                                   y=journal_keys.values,
                                   mode='lines+markers',
                                   line=dict(width=0.5),
                                   marker=dict(line=dict(width=0.1), size=7),
                                   showlegend=False),
                        row=row,
                        col=col,
                        layout_params={
                            xaxis:
                            dict(title_text="Ключевые слова", automargin=True),
                            yaxis:
                            dict(title_text="Статьи", automargin=False)
                        })
dashboard.show()

На диаграмме можно увидеть, что ключевые слова (subjects) распределенные по популярности определенные для статей, соответствуют тематикам каждого из 4-х журналов. Посмотрим на распределение слов из заголовков статей, можно ли их сопоставить с ключевыми словами.

Распределение n-gramm заголовков статей по тематикам

In [65]:
def _n_gram_value_counts(text_arr, ngram_range=(1, 1), stop_words='english'):
    cv = CountVectorizer(ngram_range=ngram_range, stop_words=stop_words)
    transformed = cv.fit_transform(text_arr)
    return pd.Series(
        data=list(np.array(transformed.sum(axis=0))[0]),
        index=list(pd.Series(
            cv.vocabulary_).sort_values().index)).sort_values(ascending=False)


# создание списка стоп-слов
stop_word_list = stopwords.words('english')
for article_variant, graph_title in zip(['title', 'full_text'],
                                        ['заголовков', 'полного текста']):

    journal_names = list(sr_prepared['journal_name'].unique())
    for n_gram, dashboard_title, v_space, xtitle in zip([(1, 1), (2, 2)],
                                                        ['слов', 'биграмм'],
                                                        [0.3, 0.53],
                                                        ['Слова', 'Биграммы']):
        dashboard = PlotlyDashboard(
            rows=2,
            cols=2,
            height=700,
            width=900,
            title=
            f'<b>Распределение {dashboard_title} {graph_title} статей по тематикам</b>',
            params=dict(vertical_spacing=v_space,
                        horizontal_spacing=0.15,
                        subplot_titles=journal_names))

        for journal_name, row, col, xaxis, yaxis in zip(
                journal_names, [1, 1, 2, 2], [1, 2, 1, 2],
            ['xaxis1', 'xaxis2', 'xaxis3', 'xaxis4'],
            ['yaxis1', 'yaxis2', 'yaxis3', 'yaxis4']):

            if article_variant == 'title':
                # Заголовки статей
                text = sr_prepared[sr_prepared['journal_name'] ==
                                   journal_name]['title']
            else:
                # Полный текст статей
                text = Enumerable(
                    sr_prepared[sr_prepared['journal_name'] == journal_name]
                    ['text'].apply(lambda x: ast.literal_eval(x))).select_many(
                        lambda x: x).to_list()

            # Извлечение n-грамм и подсчет статистики встречаемости
            journal_keys = _n_gram_value_counts(text, n_gram,
                                                stop_word_list)[:20]

            dashboard.add_board(go.Scatter(x=journal_keys.index,
                                           y=journal_keys.values,
                                           mode='lines+markers',
                                           line=dict(width=0.5),
                                           marker=dict(line=dict(width=0.1),
                                                       size=7),
                                           showlegend=False),
                                row=row,
                                col=col,
                                layout_params={
                                    xaxis:
                                    dict(title_text=xtitle, automargin=True),
                                    yaxis:
                                    dict(title_text="Количество",
                                         automargin=False)
                                })
        dashboard.show()
In [168]:
def _n_gram_idf(text_arr, ngram_range=(1, 1), stop_words='english'):
    tfidf = TfidfVectorizer(ngram_range=ngram_range, stop_words=stop_words)
    transformed = tfidf.fit_transform(text_arr)
    return pd.Series(
        data=list(np.array(transformed.sum(axis=0))[0]),
        index=list(pd.Series(
            tfidf.vocabulary_).sort_values().index)).sort_values(
                ascending=False)
In [111]:
_n_gram_idf(
    Enumerable(sr_prepared[sr_prepared['journal_name'] ==
                           'earth_and_environmental_sciences'][:100]
               ['text'].apply(lambda x: ast.literal_eval(x))).select_many(
                   lambda x: x).to_list(), (2, 2))[:25].iplot(kind='bar')
In [80]:
_n_gram_idf([
    'мама мыла раму', 'раму мыла мама', 'привет мама как дела',
    'мама думала что дела нормально'
]).iplot(kind='bar')
In [105]:
transformed = tfidf.fit_transform([
    'мама мыла раму', 'раму мыла мама', 'привет мама как дела',
    'мама думала что дела нормально'
])
list(np.array(transformed.mean(axis=0))[0])
Out[105]:
[0.21574940978110574,
 0.126691356636273,
 0.14695941276243882,
 0.3547506928589773,
 0.32021702703897603,
 0.126691356636273,
 0.14695941276243882,
 0.32021702703897603,
 0.126691356636273]
In [77]:
list(tfidf.idf_)
Out[77]:
[1.5108256237659907,
 1.916290731874155,
 1.916290731874155,
 1.0,
 1.5108256237659907,
 1.916290731874155,
 1.916290731874155,
 1.5108256237659907,
 1.916290731874155]
In [98]:
pd.Series(data=transformed.toarray().mean(axis=0)) pd.Series(tfidf.vocabulary_).sort_values()
  File "<ipython-input-98-a72fd0c14414>", line 1
    pd.Series(data=transformed.toarray().mean(axis=0)) pd.Series(tfidf.vocabulary_).sort_values()
                                                        ^
SyntaxError: invalid syntax

Распределение популярных слов текста статей

In [ ]:
#     # Удаление спец-символов
#     submbols_removed = []
#     for ww in list(article_titles):
#         for k in ww.split("\n"):
#             submbols_removed.append(re.sub(r"[^a-zA-Z0-9]+", ' ', k))

#     # Приведение к нижнему регистру
#     lower_arr = []
#     for ww in submbols_removed:
#         lower_arr.append(ww.lower())
#     # Токенизация
#     tokenized_tilts = pd.Series(lower_arr).apply(lambda x: word_tokenize(x))
#     # Удаление стоп-слов, удаление символов
#     prepared_words = [
#         w for w in [
#             z.lower() for z in Enumerable(tokenized_tilts).select_many(
#                 lambda x: x).to_list()
#         ] if w not in stop_word_list
#     ]
In [1258]:
from nltk import bigrams
from nltk import FreqDist
In [1259]:
bgms = bigrams(sr_prepared[
    sr_prepared['journal_name'] ==
    'earth_and_environmental_sciences']['title'])
In [1260]:
FreqDist(bgms)
Out[1260]:
FreqDist({('Spatio-temporal patterns and characteristics of swine shipments in the U.S. based on Interstate Certificates of Veterinary Inspection', 'A quantitative method to decompose SWE differences between regional climate models and reanalysis datasets'): 1, ('A quantitative method to decompose SWE differences between regional climate models and reanalysis datasets', 'Impact of nutritional stress on the honeybee colony health'): 1, ('Impact of nutritional stress on the honeybee colony health', 'Separating decadal global water cycle variability from sea level rise'): 1, ('Separating decadal global water cycle variability from sea level rise', 'Interannual variability of leaf area index of an evergreen conifer stand was affected by carry-over effects from recent climate conditions'): 1, ('Interannual variability of leaf area index of an evergreen conifer stand was affected by carry-over effects from recent climate conditions', 'Trait-based model development to support breeding programs. A case study for salt tolerance and rice'): 1, ('Trait-based model development to support breeding programs. A case study for salt tolerance and rice', 'Proteomic enzyme analysis of the marine fungus Paradendryphiella salina reveals alginate lyase as a minimal adaptation strategy for brown algae degradation'): 1, ('Proteomic enzyme analysis of the marine fungus Paradendryphiella salina reveals alginate lyase as a minimal adaptation strategy for brown algae degradation', 'Temperature-induced shifts in hibernation behavior in experimental amphibian populations'): 1, ('Temperature-induced shifts in hibernation behavior in experimental amphibian populations', 'Microcosm experimental evidence that habitat orientation affects phytoplankton-zooplankton dynamics'): 1, ('Microcosm experimental evidence that habitat orientation affects phytoplankton-zooplankton dynamics', 'Different bacterial communities in ectomycorrhizae and surrounding soil'): 1, ('Different bacterial communities in ectomycorrhizae and surrounding soil', 'Unraveling the Complexity of Wildland Urban Interface Fires'): 1, ...})
In [1278]:
import scipy
In [1282]:
vectorizer = CountVectorizer(ngram_range=(2, 2))
scipy.sparse.csr_matrix(vectorizer.fit_transform(sr_prepared[
    sr_prepared['journal_name'] ==
    'earth_and_environmental_sciences']['title'])).sum(axis=0)
Out[1282]:
matrix([[3, 8, 1, ..., 1, 1, 1]], dtype=int64)
In [1332]:
def _n_gram_value_counts(text_arr, ngram_range=(1, 1), stop_words='english'):
    cv = CountVectorizer(ngram_range=ngram_range, stop_words=stop_words)
    transformed = cv.fit_transform(text_arr)
    return pd.Series(
        data=list(np.array(transformed.sum(axis=0))[0]),
        index=list(pd.Series(
            cv.vocabulary_).sort_values().index)).sort_values(ascending=False)
In [1320]:
_n_gram_value_counts([
    'hello world mama papa yana dasha',
    'yana dasha mila vasya yana dasha mila vasya vasya vasya vasya'
], (2, 2)).iplot(kind='bar')
In [1299]:
cv = CountVectorizer(ngram_range=(2, 2))
transformed = cv.fit_transform([
    'hello world mama papa yana dasha',
    'yana dasha mila vasya yana dasha mila vasya'
])
transformed.toarray()
Out[1299]:
array([[0, 1, 1, 0, 1, 0, 1, 1],
       [2, 0, 0, 2, 0, 1, 0, 2]])
In [1310]:
list(np.array(transformed.sum(axis=0))[0])
Out[1310]:
[2, 1, 1, 2, 1, 1, 1, 3]
In [1292]:
pd.Series(cv.vocabulary_).sort_values()
Out[1292]:
dasha mila     0
hello world    1
mama papa      2
mila vasya     3
papa yana      4
vasya yana     5
world mama     6
yana dasha     7
dtype: int64
In [ ]:
 
In [1269]:
pd.Series(vectorizer.vocabulary_).sort_values()
Out[1269]:
000 year                0
000 years               1
001 facets              2
001 nanofacets          3
001l and                4
                    ...  
κb is               91784
κb mediated         91785
μm and              91786
μm scale            91787
μpad application    91788
Length: 91789, dtype: int64
In [ ]:
pd.Series(
    Enumerable(
        sr_prepared[sr_prepared['journal_name'] ==
                    'earth_and_environmental_sciences']
        ['subjects']).select(lambda x: ast.literal_eval(x)).select_many(
            lambda x: x).to_list()).value_counts()
In [ ]:
sr_prepared[sr_prepared['journal_name'] ==
                    'earth_and_environmental_sciences']
In [1150]:
pd.Series(
    Enumerable(
        sr_prepared[sr_prepared['journal_name'] ==
                    'earth_and_environmental_sciences']
        ['subjects']).select(lambda x: ast.literal_eval(x)).select_many(
            lambda x: x).to_list()).value_counts()[:25]
In [1135]:
sr_prepared.head(3)
Out[1135]:
journal_name title authors abstract aff_authors subjects biblio text country_name country_lat country_lon city_name city_lat city_lon
0 earth_and_environmental_sciences Spatio-temporal patterns and characteristics o... ['Erin E. Gorsich', 'Ryan S. Miller', 'Holly M... Domestic swine production in the United States... ['Erin E. Gorsich,\xa0Ryan S. Miller,\xa0Holly... ['Ecology', 'Risk factors'] ['Received14 May 2018', 'Accepted24 January 20... ['Swine production in the United States is a 1... United States of America###United States of Am... 37.09024###37.09024###55.378051###55.378051###... -95.712891###-95.712891###-3.435973###-3.43597... Fort Collins###Fort Collins###Coventry###Coven... 40.5852778###40.5852778###52.416667###52.41666... -105.08388889999999###-105.08388889999999###-1...
1 earth_and_environmental_sciences A quantitative method to decompose SWE differe... ['Yun Xu', 'Andrew Jones', 'Alan Rhoades'] The simulation of snow water equivalent (SWE) ... ['Yun Xu,\xa0Andrew Jones\xa0&\xa0Alan Rhoades'] ['Cryospheric science', 'Hydrology'] ['Received24 January 2019', 'Accepted23 Octobe... ['Snowpack is a key component of the Earth’s c... United States of America 37.09024 -95.712891 Berkeley 37.8716667 -122.27166670000001
2 earth_and_environmental_sciences Impact of nutritional stress on the honeybee c... ['B. Branchiccela', 'L. Castelli', 'M. Corona'... Honeybees Apis mellifera are important pollina... ['B. Branchiccela,\xa0L. Castelli,\xa0G. Martí... ['Microbial ecology', 'Pathogens'] ['Received28 January 2019', 'Accepted20 June 2... ['Insects play a significant role in the funct... Uruguay###United States of America###Uruguay##... -32.522779###37.09024###-32.522779###-32.522779 -55.765834999999996###-95.712891###-55.7658349... Montevideo###Maryland###Uruguay###Montevideo -34.85805560000001###42.0655556###-32.4833333#... -56.1708333###-89.54722220000001###-53.5166667...
In [1138]:
pd.DataFrame({
    'geo': [10, 8, 6],
    'meo': [5, 8, 6],
    'zeo': [2, 1, 17]
},
             index=[2001, 2002, 2003]).iplot(kind='bar')